#include <jni.h>
#include <string.h>
#include <android/log.h>
#include "jni_method.h"
#include <pthread.h>

/*
 * 定义LOG函数
 */
#define TAG "JniMethod"

#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, TAG, __VA_ARGS__)


#ifndef NELEM
 #define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0])))
#endif

// 全局变量
JNIEnv *g_env;
jclass native_class;
JavaVM *g_vm;
pthread_mutex_t thread_mutex;

/*
 * Class:     com_example_jnitest_TestJniMethods
 * Method:    test
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_jnitest_TestJniMethods_test(JNIEnv *env, jobject object)
{
	int version = env->GetVersion();
	LOGV("GetVersion() --> jni version:%2x", version);

	jclass build_class = env->FindClass("android/os/Build");									// 加载一个本地类
	jfieldID brand_id = env->GetStaticFieldID(build_class, "MODEL", "Ljava/lang/String;");		// 获取类的静态字段ID
	jstring brand_obj = (jstring)env->GetStaticObjectField(build_class, brand_id);				// 获取类的静态字段的值

	const char* nativeString = env->GetStringUTFChars(brand_obj, 0);							// 通过jstring生成char*，转成UTF-8格式
	LOGV("GetStringUTFChars() --> MODEL:%s", nativeString);
	env->ReleaseStringUTFChars(brand_obj, nativeString);										// 释放GetStringUTFChars()生成的char*

	jclass test_class = env->FindClass("com/example/jnitest/TestClass");
	if(env->ExceptionCheck()) 
	{
		env->ExceptionDescribe();				// 输出异常
		env->ExceptionClear();					// 清除异常
		LOGE("ExceptionCheck()");
		LOGE("ExceptionDescribe()");
		LOGE("ExceptionClear()");
		return;
	}
	jmethodID constructor = env->GetMethodID(test_class, "<init>", "()V");						// 获取构造函数

	/*
	 * 创建对象的第一种方法，调用NewObject，把构造函数当成参数传递进去 
	 */
	jobject obj = env->NewObject(test_class, constructor);										// 创建一个对象，当构造函数当成参数传递进去

	jthrowable throwable = env->ExceptionOccurred();			// 获取异常对象，如果没异常发生返回NULL
	if (throwable)												// 有异常发生，也可以使用ExceptionCheck()函数来判断
	{
		env->ExceptionDescribe();
		env->ExceptionClear();
		LOGE("ExceptionOccurred()");
	}

	if (obj)
	{
		env->MonitorEnter(obj);				// 同步操作

		jfieldID stringfieldID = env->GetFieldID(test_class, "aStringField", "Ljava/lang/String;");			// 获取String类型的字段aStringField
		jstring stringfieldValue = (jstring)env->GetObjectField(obj, stringfieldID);						// 获取aStringField字段的值
		const char *stringValue = env->GetStringUTFChars(stringfieldValue, 0);
		LOGV("GetObjectField() --> aStringField:%s", stringValue);
		env->ReleaseStringUTFChars(stringfieldValue, stringValue);

		const char* myValue = "def";
		env->SetObjectField(obj, stringfieldID, env->NewStringUTF(myValue));	// 设置字段的值
		LOGV("SetObjectField() --> aStringField:def");

		jfieldID intfieldID = env->GetFieldID(test_class, "aIntField", "I");	// 获取int类型的字段aIntField
		jint fieldValue = env->GetIntField(obj, intfieldID);					// 获取aIntField字段的值
		LOGV("GetIntField() --> aField:%d", fieldValue);
		env->SetIntField(obj, intfieldID, (jint)123);							// 设置int字段的值
		fieldValue = env->GetIntField(obj, intfieldID);
		LOGV("SetIntField() --> aField:%d", fieldValue);

		jclass parent_class = env->GetSuperclass(test_class);					// 获取父类
		LOGV("GetSuperClass() --> ");

		if (JNI_OK == env->EnsureLocalCapacity(5))								// 检测是否还可以创建5个局部引用
		{
			LOGV("EnsureLocalCapacity() --> ensure 5 locals");
			jmethodID obj2_voidmethod = env->GetMethodID(test_class, "aVoidMethod", "()V");									// 获取一个void方法
			env->CallVoidMethod(obj, obj2_voidmethod);
			LOGV("CallVoidMethod()");

			jmethodID obj2_Staticmethod = env->GetStaticMethodID(test_class, "aStaticMethod", "(Ljava/lang/String;)V");		// 获取一个static方法
			LOGV("GetStaticMethodID()");
			const char* fromJni = "this string from jni";
			jstring jstr_static = env->NewStringUTF(fromJni);							// char*转jstring
			env->CallStaticVoidMethod(test_class, obj2_Staticmethod, jstr_static);		// 调用一个静态方法

			jmethodID obj2_chinesemethod = env->GetMethodID(test_class, "getChineseString", "()Ljava/lang/String;");		// 传递中文字符串
			jstring obj2_chinesejstring = (jstring)env->CallObjectMethod(obj, obj2_chinesemethod);
			jsize chinese_size = env->GetStringLength(obj2_chinesejstring);				// 返回Java字符串的长度（Unicode 字符数）
			LOGV("GetStringLength() --> %d", chinese_size);
			jchar buff_jchar[4] = {0};
			env->GetStringRegion(obj2_chinesejstring, 0, 3, buff_jchar);		// bufchar的值是啥
			LOGV("GetStringRegion() --> 特别关注");
			const jchar* obj2_chinesechars = env->GetStringChars(obj2_chinesejstring, NULL);		// jstring转char*，转成Unicode格式
			jstring new_chinesestring = env->NewString(obj2_chinesechars, chinese_size);
			env->CallStaticVoidMethod(test_class, obj2_Staticmethod, new_chinesestring);
			env->ReleaseStringChars(obj2_chinesejstring, obj2_chinesechars);						// 释放GetStringChars获取的jchar*
			LOGV("CallStaticVoidMethod()");

			jmethodID obj2_sqrtmethod = env->GetStaticMethodID(test_class, "sqrt", "(I)I");
			jint int_sqrt = env->CallStaticIntMethod(test_class, obj2_sqrtmethod, (jint)5);			// 计算5的平方
			LOGV("CallStaticIntMethod() -->5 sqrt is:%d", int_sqrt);
		}

		if (JNI_TRUE == env->IsAssignableFrom(test_class, parent_class))				// 判断test_class类型是否可安全地强制转换为parent_class类型
		{
			LOGV("IsAssignableFrom() --> yes");
		}
		else
		{
			jclass newExceptionClazz = env->FindClass("java/lang/RuntimeException");	// 获取一个异常类型
			if (newExceptionClazz != NULL)
			{
				env->ThrowNew(newExceptionClazz, "这里不会被执行");						// 实例化异常对象并抛出该异常
				LOGE("ThrowNew()");
			}
		}

		jclass obj_clazz = env->GetObjectClass(obj);				// 获取对象的类型
		if (JNI_TRUE == env->IsInstanceOf(obj, obj_clazz))			// 测试对象是否为某个类的实例
		{
			LOGV("IsInstanceOF() --> Yes");
		}
		else
		{
			env->FatalError("fatal error!");						// 抛出致命错误并且不希望虚拟机进行修复
			LOGE("FatalError()");
		}

		env->PushLocalFrame(2);										// 申请局部引用空间
		jobject obj_localref = env->NewLocalRef(obj);				// 创建一个局部引用
		jobject obj_globalref = env->NewGlobalRef(obj);				// 创建一个全局引用
		LOGV("PushLocalFrame()");
		LOGV("NewLocalRef()");
		LOGV("NewGlobalRef()");
		if (JNI_TRUE == env->IsSameObject(obj_localref, obj_globalref))			// 测试两个引用是否引用同一个Java对象
		{
			LOGV("IsSameObject() --> Yes");
		}
		env->DeleteLocalRef(obj_localref);							// 删除一个局部引用
		env->DeleteGlobalRef(obj_globalref);						// 删除一个全局引用
		env->PopLocalFrame(NULL);									// 释放前面申请的局部引用空间
		LOGV("DeleteLocalRef()");
		LOGV("DeleteGlobalRef()");
		LOGV("PopLocalFrame()");

		env->MonitorExit(obj);										// 离开临界区
	}


	jclass sub_class = env->FindClass("com/example/jnitest/TestSubClass");

	/*
	 * 创建对象的第二种方法，直接给对象分配内存，（再手动调用构造函数，也可以不调用）
	 */
	 jobject sub_obj = env->AllocObject(sub_class);					// 分配内存
	 jmethodID sub_methodID = env->GetMethodID(sub_class, "aVoidMethod", "()V");
	 env->CallNonvirtualVoidMethod(sub_obj, sub_class, sub_methodID);				// 调用子类里的方法
	 env->CallNonvirtualVoidMethod(sub_obj, test_class, sub_methodID);				// 根据调用类的不同调用父类的方法

	 jfieldID sub_fieldID = env->GetStaticFieldID(sub_class, "subFloatField", "F");	// 获取子类静态字段
	 env->SetStaticFloatField(sub_class, sub_fieldID, (jfloat)33.88f);
	 LOGV("SetStaticFloatField() --> %.2f", env->GetStaticFloatField(sub_class, sub_fieldID));

	 jclass class_string = env->FindClass("java/lang/String");
	 jobjectArray string_array = env->NewObjectArray(3, class_string, 0);	// 创建String数组
	 LOGV("NewObjectArray()");
	 jsize array_size = env->GetArrayLength(string_array);			// 获取数组中元素的个数
	 LOGV("GetArrayLength() --> %d", array_size);
	 jstring array_string1 = env->NewStringUTF("one");
	 char buff_char[4] = {0};
	 env->GetStringUTFRegion(array_string1, 0, 3, buff_char);
	 LOGV("GetStringUTFRegion() --> %s", buff_char);
	 env->SetObjectArrayElement(string_array, 0, array_string1);	// 设置数组元素的值
	 LOGV("SetObjectArrayElement() --> one");
	 array_string1 = (jstring)env->GetObjectArrayElement(string_array, 0);		// 获取数组元素的值
	 const char* array_elemchars = env->GetStringUTFChars(array_string1, NULL);
	 LOGV("GetObjectArrayElement() --> %s", array_elemchars);
	 env->ReleaseStringUTFChars(array_string1, array_elemchars);

	 jintArray int_array = env->NewIntArray(5);						// 创建int数组
	 LOGV("NewIntArray() --> %d", env->GetArrayLength(int_array));	// 获取数组长度
	 const jint ints[] = {11, 12, 13, 14, 15};
	 env->SetIntArrayRegion(int_array, 0, 5, ints);		// 设置数组一个范围的值
	 LOGV("SetIntArrayRegion() --> %d,%d,%d,%d,%d", 
	 	ints[0], ints[1], ints[2], ints[3], ints[4]);

	 jint ints2[2] = {0, 0};
	 env->GetIntArrayRegion(int_array, 1, 2, ints2);	// 获取数组一个范围的值
	 LOGV("GetIntArrayRegion() --> %d,%d", ints2[0], ints2[1]);

	 jint* array_ints = env->GetIntArrayElements(int_array, NULL);	// 返回指向所有元素的指针
	 LOGV("GetIntArrayElements() --> %d,%d,%d,%d,%d",
	 	array_ints[0], array_ints[1], array_ints[2], array_ints[3], array_ints[4]);
	 env->ReleaseIntArrayElements(int_array, array_ints, 0);		// 释放指向所有元素的指针
	 LOGV("ReleaseIntArrayElements()");

}


JNIEXPORT jstring nativeMethod(JNIEnv* env, jobject object)
{
	const char* chs = "你好！NativeMethod";
	return env->NewStringUTF(chs);
}


// 线程函数
void* thread_func(void *arg)
{
	JNIEnv *env;
	pthread_mutex_lock(&thread_mutex);				// 锁定互斥锁
	if (JNI_OK != g_vm->AttachCurrentThread(&env, NULL))		// 附加当前线程到Java(Dalvik)虚拟机，一个线程必须附加到虚拟机，在任何其他JNI可调用之前
	{
		LOGE("AttachCurrentThread() failed");
		return NULL;
	}
	LOGV("AttachCurrentThread() --> thread:%d", (jint)arg);
	g_vm->DetachCurrentThread();					// 分离当前线程，退出虚拟机之前，线程必须被分离
	LOGV("DetachCurrentThread() --> thread:%d", (jint)arg);
	pthread_mutex_unlock(&thread_mutex);			// 释放互斥锁
	pthread_exit(0);
	return NULL;

}

JNIEXPORT void newJniThreads(JNIEnv* env, jobject obj, jint nums)
{
	env->GetJavaVM(&g_vm);				// 获取和当前线程相关的JavaVM
	pthread_t* pt = (pthread_t*)malloc(sizeof(pthread_t) * nums);			// 分配空间
	pthread_mutex_init(&thread_mutex, NULL);		// 初始化线程互斥锁
	int i;
	for (i = 0; i < nums; ++i)
	{
		pthread_create(&pt[i], NULL, &thread_func, (void*)i);				// 创建线程
	}
	free(pt);
}


JNIEXPORT jobject allocNativeBuffer(JNIEnv* env, jobject obj, jlong size)
{
	void* buffer = malloc(size);
	jobject directBuffer = env->NewDirectByteBuffer(buffer, size);
	LOGV("NewDirectByteBuffer() --> %d",(int)size);
	return directBuffer;
}

JNIEXPORT void freeNativeBuffer(JNIEnv* env, jobject obj, jobject bufferRef)
{
	char *buffer = (char*)env->GetDirectBufferAddress(bufferRef);
	strcpy(buffer, "123");
	LOGV("GetDirectBufferAddress() --> %s", buffer);
	free(buffer);
}


static JNINativeMethod g_methods[] = {
	{"nativeMethod", "()Ljava/lang/String;", (void*)nativeMethod},
	{"newJniThreads", "(I)V", (void*)newJniThreads},
	{"allocNativeBuffer", "(J)Ljava/lang/Object;", (void*)allocNativeBuffer},
	{"freeNativeBuffer", "(Ljava/lang/Object;)V", (void*)freeNativeBuffer}
};

/*
 * System.loadLibrary() 时调用 
 * 如果成功返回 JNI 版本，失败返回 -1 
 */
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
	if(JNI_OK != vm->GetEnv((void**)&g_env, JNI_VERSION_1_6))			// 加载指定版本的JNI
	{
		return -1;
	}
	LOGV("JNI_OnLoad()");

	native_class = g_env->FindClass("com/example/jnitest/TestJniMethods");
	if (JNI_OK == g_env->RegisterNatives(native_class, g_methods, NELEM(g_methods)))	// 注册未声明的本地方法
	{
		LOGV("RegisterNatives() --> nativeMethod() ok");
	}
	else
	{
		LOGE("RegisterNatives() --> nativeMethod() failed");
		return -1;
	}
	return JNI_VERSION_1_6;
}